#include "limbo/ip/state.hpp"
#include "limbo/stack.hpp"
#include "limbo/udp/state.hpp"
#include "test-utils.h"

using namespace limbo;

uint8_t payload[] = {0x14, 0xe9, 0x14, 0xe9, 0x00, 0x24, 0xb6, 0xaa, 0x00,
                     0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
                     0x00, 0x00, 0x04, 0x77, 0x70, 0x61, 0x64, 0x05, 0x6c,
                     0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x01, 0x00, 0x01};
auto payload_chunk = Chunk(payload, sizeof(payload));

auto src = make_address("10.0.31.124");
auto dst = make_address("224.0.0.251");

using IP = ip::State<void, ip::Opts::calculate_checksum>;
using UDP = udp::State<IP>;
using MyStack = Stack<Layer<UDP>, Layer<IP>>;

static uint32_t copy_counter = 0;

namespace limbo {

template <> struct customization<IP> {
  static void *copy(void *dest, const void *src, std::size_t count) {
    ++copy_counter;
    return customization<void>::copy(dest, src, count);
  }

  static void copy(Chunk &dest, const Chunk source) {
    ++copy_counter;
    return customization<void>::copy(dest, source);
  }
};

} // namespace limbo

TEST_CASE("send ip", "[customization]") {
  using Context = typename IP::Context;
  using Packet = typename IP::Packet;
  char buff[100];
  auto buff_chunk = Chunk(buff, sizeof(buff));
  auto ctx = Context{src, dst, 1, ip::Proto::udp, 0, 0, nullptr};
  auto state = IP();

  copy_counter = 0;
  state.init(0x82DD);

  auto result = Packet::send(state, ctx, buff_chunk, payload_chunk);
  REQUIRE(result);
  CHECK(copy_counter == 2);
}

TEST_CASE("send udp", "[customization]") {
  copy_counter = 0;
  auto stack = MyStack();
  auto &ip_state = stack.get<1, 0>();
  ip_state.init(0x04);

  auto ip_ctx = IP::Context{src, dst, 6, ip::Proto::udp, 0, 0, nullptr};
  auto udp_ctx = UDP::Context{7, 8, &ip_ctx};
  unsigned char buff[100];

  auto buff_chunk = Chunk(buff, sizeof(buff));
  auto result = stack.send(buff_chunk, udp_ctx, payload_chunk);
  REQUIRE(result);
  CHECK(copy_counter == 4);
}
